﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using LCA.Community.Web.Filters;
using LCA.Community.Web.Models;
using Microsoft.Practices.Unity;
using LCA.Community.DAL.Interfaces;
using LCA.Community.DAL.Model;

namespace LCA.Community.Web.Controllers
{
    /// <summary>
    /// Controller for managing groups.
    /// </summary>
    public class GroupController : BaseController
    {
        /// <summary>
        /// Index action showing the home page of the group.
        /// </summary>
        /// <param name="id">The group ID.</param>
        /// <returns>View of the group home page.</returns>
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NotLoggedIn")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Index(string id)
        {
            var group = GroupsRepository.Get(id);
            var groupViewModel = MvcApplication.Container.Resolve<GroupViewModel>(new ParameterOverrides { { "group", group } });

            ViewData["owner"] = IsGroupOwner(id);
            ViewData["member"] = IsGroupMember(id);
            ViewData["name"] = id;
            return View("Index", groupViewModel);
        }

        /// <summary>
        /// Action for a user to leave a group
        /// </summary>
        /// <param name="userID">The user id</param>
        /// <param name="id">The group</param>
        /// <returns>View of the user home page.</returns>
        [SecurityFilter(SecurityRequisites = "GroupOwner,GroupMember", SolvingActionParameter = "groupID", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        [ErrorFilter(typeof(ArgumentException), "NoAuthorized", 3)]
        public ActionResult LeaveGroup(string id, string userID)
        {
            var group = GroupsRepository.Get(id);

            if (!IsOwner(userID))
            {
                throw new ArgumentException();
            }
            else if (group.Owner == UserID)
            {
                GroupsRepository.Delete(group);
            }
            else
            {
                GroupMembershipRepository.Delete(id, userID);
            }
            return RedirectToAction("Index", "User", new { userID });
        }

        /// <summary>
        /// Action for a user to leave multiple groups.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="selectedgroups">The selected groups.</param>
        /// <returns>Partial view of the groups that the user is member of.</returns>
        [SecurityFilter(SecurityRequisites = "Owner", SolvingActionParameter = "userID", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "Se produjo un error al procesar la solicitud", 1)]
        [ErrorFilter(typeof(NullReferenceException), "El usuario referenciado no existe", 2)]
        public ActionResult LeaveGroups(string userID, string[] selectedgroups)
        {
            foreach (String groupRowKey in selectedgroups)
            {
                var group = GroupsRepository.Get(groupRowKey);

                if (!IsOwner(userID))
                {
                    throw new ArgumentException();
                }
                else if (group.Owner == userID)
                {
                    GroupsRepository.Delete(group);
                }
                else
                {
                    GroupMembershipRepository.Delete(groupRowKey, userID);
                }
            }

            var partialView = new PartialViewResult();
            partialView.ViewName = "ShowUserGroups";
            partialView.ViewData.Model = GroupsRepository.GetGroupsByUser(userID);
            partialView.ViewData["owner"] = IsOwner(userID);
            return partialView;
        }

        /// <summary>
        /// Action for removing the selected members from the group.
        /// </summary>
        /// <param name="groupID">The group ID.</param>
        /// <param name="selectedusers">Selected users.</param>
        /// <returns>Partial view with the members of the group.</returns>
        [AcceptVerbs(HttpVerbs.Post)]
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "groupID", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult RemoveMembers(string groupID, string[] selectedusers)
        {
            foreach (String item in selectedusers)
                GroupMembershipRepository.Delete(groupID, item);

            var partialView = new PartialViewResult();
            partialView.ViewName = "ShowUsers";
            partialView.ViewData.Model = GroupsRepository.GetGroupMembers(groupID);
            partialView.ViewData["owner"] = IsGroupOwner(groupID);
            partialView.ViewData["member"] = IsGroupMember(groupID);
            return partialView;
        }

        /// <summary>
        /// Action for sending a message to the owner of a group asking for being a member of the group.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="id">The group ID.</param>
        /// <returns>View of the group home page.</returns>
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NotLoggedIn")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult RequestJoin(string id)
        {
            var message = MessagesRepository.GetJoiningRequestMessage(id, UserID);
            if (message == null)
            {
                CreateJoiningRequestEmail(UserID, id);
            }

            return RedirectToAction("Index", "Group", new { id });
        }

        /// <summary>
        /// Action for accepting the request from a user to join a group.
        /// </summary>
        /// <param name="userID">The user id.</param>
        /// <param name="id">The group id.</param>
        /// <returns>View of the inbox of the group.</returns>
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "groupID", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult AcceptGroupMembershipRequest(string groupID, string userID)
        {
            CreateJoiningRequestAcceptedEmail(userID, groupID);
            GroupMembershipRepository.Add(groupID, userID);

            var message = MessagesRepository.GetJoiningRequestMessage(groupID, userID);
            MessagesRepository.Delete(message);

            return RedirectToAction("Index", "Mailbox", new { id = groupID });
        }

        /// <summary>
        /// Action for denying a group membership request.
        /// </summary>
        /// <param name="id">The group ID.</param>
        /// <param name="userID">The user ID.</param>
        /// <returns>View for mailbox home page.</returns>
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "groupID", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult DenyGroupMembershipRequest(string groupID, string userID)
        {
            CreateJoiningRequestDeniedEmail(userID, groupID);

            var message = MessagesRepository.GetJoiningRequestMessage(groupID, userID);
            MessagesRepository.Delete(message);

            return RedirectToAction("Index", "Mailbox", new { id = groupID });
        }

        /// <summary>
        /// Action for removing a group.
        /// </summary>
        /// <param name="groupID">The group ID.</param>
        /// <param name="userID">The user ID.</param>
        /// <returns>View of admin home page if the user is admin, otherwise user home page.</returns>
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "id", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Remove(string id)
        {
            GroupsRepository.Delete(id);
            if (IsAdmin(UserID))
            {
                return RedirectToAction("Index", "Admin");
            }
            else 
            {
                return RedirectToAction("Index", "User", new { UserID });
            }
        }

        /// <summary>
        /// Action for editing a group profile
        /// </summary>
        /// <param name="id">The group ID.</param>
        /// <returns>View for editing a group profile.</returns>
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "id", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Edit(string id)
        {
            var group = GroupsRepository.Get(id);
            var user = UsersRepository.Get(UserID);

            ViewData["owner"] = IsGroupOwner(id);
            ViewData["member"] = IsGroupMember(id);
            ViewData["user"] = user;

            return View("Edit", group);
        }

        /// <summary>
        /// Aciton that receives the request for editing a group.
        /// </summary>
        /// <param name="group">The group.</param>
        /// <returns>View of the home page of the group if everything is ok, otherwise view for creating a group.</returns>
        [AcceptVerbs(HttpVerbs.Post)]
        [SecurityFilter(SecurityRequisites = "GroupOwner", SolvingActionParameter = "id", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Edit(string id,Group group)
        {
            if (ModelState.IsValid)
            {
                group.Owner = UserID;
                group.RowKey = id;
                group.PartitionKey = group.RowKey;
                if (AvatarManager.HasAvatar(Request))
                {
                    group.Avatar = AvatarManager.GetGroupAvatarUrl(group.RowKey, Request, Url);
                    AvatarManager.SaveGroupAvatar(group.RowKey, Request);
                }
                GroupsRepository.Update(group);
                return RedirectToAction("Index", "Group", new { id = group.RowKey });
            }

            var user = UsersRepository.Get(UserID);
            ViewData["owner"] = IsGroupOwner(group.RowKey);
            ViewData["member"] = IsGroupMember(group.RowKey);
            ViewData["user"] = user;
            return View("Edit", group);
        }

        /// <summary>
        /// Action for creating a group.
        /// </summary>
        /// <returns>View for creating a group.</returns>
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "ErrorRequest", 1)]
        [ErrorFilter(typeof(NullReferenceException), "UserNotFound", 2)]
        public ActionResult Create()
        {
            var user = UsersRepository.Get(UserID);

            ViewData["friends"] = IsFriend(UserID);
            ViewData["owner"] = IsOwner(UserID);
            ViewData["user"] = user;

            return View("Create", new Group());
        }

        /// <summary>
        /// Action that receives the request for creating a group
        /// </summary>
        /// <param name="group">The group.</param>
        /// <returns>View of the home page of the group if everything is ok, otherwise view for creating a group.</returns>
        [AcceptVerbs(HttpVerbs.Post)]
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NoAuthorized")]
        [ErrorFilter(typeof(Exception), "ErrorRequest", 1)]
        [ErrorFilter(typeof(NullReferenceException), "UserNotFound", 2)]
        public ActionResult Create(Group group)
        {
            if (ModelState.IsValid)
            {
                if (GroupsRepository.Get(group.RowKey) != null)
                {
                    ModelState.AddModelError("Group.Alias", "El identificador de grupo ya está en uso.");
                    group.RowKey = null;
                }
                else
                {
                    group.Owner = UserID;
                    group.PartitionKey = group.RowKey;
                    group.Avatar = AvatarManager.GetGroupAvatarUrl(group.RowKey, Request, Url);
                    GroupsRepository.Add(group);
                    AvatarManager.SaveGroupAvatar(group.RowKey, Request);
                    return RedirectToAction("Index", "Group", new { id = group.RowKey });
                }
            }

            var user = UsersRepository.Get(UserID);
            ViewData["friends"] = IsFriend(UserID);
            ViewData["owner"] = IsOwner(UserID);
            ViewData["user"] = user;

            return View("Create", group);
        }

        /// <summary>
        /// Action for showing the profile of the group.
        /// </summary>
        /// <param name="id">The group id.</param>
        /// <returns>View showing the profile of the group.</returns>
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NotLoggedIn")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Profile(string id)
        {
            var group = GroupsRepository.Get(id);

            ViewData["owner"] = IsGroupOwner(id);
            ViewData["member"] = IsGroupMember(id);

            return View("Profile", group);
        }

        /// <summary>
        /// Action for showing the members of a group.
        /// </summary>
        /// <param name="id">The group id.</param>
        /// <returns>View showing the members of the group.</returns>
        [SecurityFilter(SecurityRequisites = "Registered", ErrorResource = "NotLoggedIn")]
        [ErrorFilter(typeof(Exception), "RequestError", 1)]
        [ErrorFilter(typeof(NullReferenceException), "GroupNotFound", 2)]
        public ActionResult Members(string id)
        {
            var group = GroupsRepository.Get(id);
            var groupViewModel = MvcApplication.Container.Resolve<GroupViewModel>(new ParameterOverrides { { "group", group } });
            
            ViewData["owner"] = IsGroupOwner(id);
            ViewData["member"] = IsGroupMember(id);

            return View("Members", groupViewModel);
        }
        #region Private methods
        /// <summary>
        /// Creates a joining request email.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="groupID">The group ID.</param>
        private void CreateJoiningRequestEmail(string userID, string groupID)
        {
            string acceptLink = string.Format("<a href=\"{0}\">Aceptar<a />", Url.Action("AcceptGroupMembershipRequest", "Group", new { groupID, userID }));
            string denyLink = string.Format("<a href=\"{0}\">Rechazar<a />", Url.Action("DenyGroupMembershipRequest", "Group", new { groupID, userID }));
            string body = string.Format("El usuario {0} solicitó unirse al grupo {1}.\nClick {2} o {3}.", userID, groupID, acceptLink, denyLink);
            var joinProposal = new Message(userID, MessagesRepository.JoiningRequestSubject, body, new List<string> { groupID });

            MessagesRepository.Add(joinProposal);
        }

        /// <summary>
        /// Created joining request accepted email.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="groupID">The group ID.</param>
        private void CreateJoiningRequestAcceptedEmail(string userID, string groupID)
        {
            string body = string.Format("El grupo {0} aceptó tu petición para unirte.", groupID);
            var acceptMessage = new Message(groupID, "Petición aceptada", body, new List<string> { userID });
            MessagesRepository.Add(acceptMessage);
        }

        /// <summary>
        /// Created joining request denied email.
        /// </summary>
        /// <param name="userID">The user ID.</param>
        /// <param name="groupID">The group ID.</param>
        private void CreateJoiningRequestDeniedEmail(string userID, string groupID)
        {
            string body = string.Format("El grupo {0} denego tu petición de ingreso.", groupID);
            var denyMessage = new Message(groupID, "Peticion de ingreso denegada", body, new List<string> { userID });
            MessagesRepository.Add(denyMessage);
        }
        #endregion

        [Dependency]
        public IUsersRepository UsersRepository { get; set; }
        [Dependency]
        public IMessagesRepository MessagesRepository { get; set; }
        [Dependency]
        public AvatarManager AvatarManager { get; set; }
    }
}
